//==============================================================================
//
//         DTable - the 2d - array collection in the ofc-library
//
//               Copyright (C) 2003  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2008-08-05 16:29:04 $ $Revision: 1.7 $
//
//==============================================================================

#include "ofc/DTable.h"


#if _INTERFACE_

#include "ofc/config.h"

//
// The table collection stores objects in an 2d array that can grow. 
// Objects can be accessed by an index. Due to the easy access there 
// is no iterator.
//
// @example
// #include <stdio.h>
// #include "ofc/DTable.h"
// #include "ofc/DInt.h"
// 
// int main(int argc, char *argv[])
// {
//   DTable *tab1 = [DTable alloc];
//   DTable *tab2 = [DTable new  ];
//   DInt   *nr;
//   int     c,r;
// 
//   [tab1 init :5 :4];                   // Init the table with 5 columns and 4 rows
// 
//   printf("Length (%ld), columns(%d) and rows(%d) for table1.\n", [tab1 length], [tab1 columns], [tab1 rows]);
// 
//   for (c = 0; c < [tab1 columns]; c++) // Fill the whole table with integers
//   {
//     for (r = 0; r < [tab1 rows]; r++)
//     {
//       nr = [DInt new]; 
//       
//       [nr set :(c*r)];
// 
//       [tab1 set :c :r :nr];
//     }
//   }
// 
//   nr = [tab1 get :4 :3];
//   printf("Element (4,3) has value:%d.\n", [nr get]);
//   
// 
//   
//   [tab2 columns :4];                  // Set the dimensions of table2
//   [tab2 rows    :4];
//   
//   nr = [DInt alloc]; [nr init :7];
//                                       // Set the object in table2
//   [tab2 set :0 :0 :nr];
//   [tab2 set :1 :1 :nr];
//   [tab2 set :2 :2 :nr];
//   [tab2 set :3 :3 :nr];
// 
//   printf("Table2 %s object nr7.\n", ([tab2 has :nr] ? "has" : "has not"));
// 
//   printf("Table2 has object nr7 %ld times.\n", [tab2 count :nr]);
//   
//   [tab1 free];                         // Cleanup
//   [tab2 shallowFree];                  // Cleanup only table, nr is repeated present
//   [nr   free];
// 
//   return 0;
// }
// 

@interface DTable : DObject
{
@private
  id            *_objects;  // the stored objects
  int            _columns;  // the number of columns
  int            _rows;     // the number of rows
  long           _length;   // the length of the table
}

#endif



@implementation DTable


//// Constructors

//
// Initialise an empty table
//
// @return the object
//

- (DTable *) init
{
  [self init :1 :1];

  return self;
}

//
// Initialise a table with columns and rows
//
// @param columns  the number of columns
// @param rows     the number of rows
//
// @return the object
//

- (DTable *) init :(int) columns :(int) rows
{
  long i;
   
  [super init];

  if (columns < 1)
  {
    WARNING(DW_INVALID_ARG, "columns");
    columns = 1;
  }
  if (rows < 1)
  {
    WARNING(DW_INVALID_ARG, "rows");
    rows = 1;
  }
    
  _columns = columns;
  _rows    = rows;
  _length  = _columns * _rows;
   
  _objects = (id *) objc_malloc(sizeof(id) * _length);
  
    
  for (i = 0; i < _length; i++)
  {
    _objects[i] = nil;
  }

  return self;
}


//// Copy related methods

//
// Do a shallow copy of the object
// 
// @return the object
// 
- shallowCopy
{
  long i;
  
  DTable *copy = [super shallowCopy];
  
  copy->_objects = (id *) objc_malloc(sizeof(id) * _length);
  
  for (i = 0; i < _length; i++)
  {
    copy->_objects[i] = _objects[i];
  }
  
  return copy;
}
  
//
// Deepen a copy of the table
//
// @return the object
//
- deepen
{
  long  i;
  
  [super deepen];
  
  for (i = 0; i < _length; i++)
  {
    _objects[i] = [_objects[i] copy];
  }
  
  return self;
}


//// Destructor

//
// Free the table, not the stored objects
//
// @return the object
//

- shallowFree
{
  long i;

  for (i = 0; i < _length; i++)
  {
    _objects[i] = nil;
  }
  
  objc_free(_objects);
  
  [super free];

  return self;
}

//
// (Deep) free the table (incl. the objects)
//
// @return the object
//

- free
{
  long i;

  for (i = 0; i <_length; i++)
  {
    if (_objects[i] != nil)
    {
      [_objects[i] free];
    }
  }

  [self shallowFree];

  return self;
}


//// Member methods

//
// Return the length of the table (columns * rows)
//
// @return the length
//

- (long) length
{
  return _length;
}


//
// Return the number of columns
// 
// @return the number of columns
// 

- (int) columns
{
  return _columns;
}

//
// Set the number of columns
// 
// @param columns  the number of columns
// 
// @return the object
// 

- (DTable *) columns :(int) columns
{
  if (columns > _columns)
  {
    long  length = _rows * columns;
    long  i;
    long  row, col;
    
    id   *origin = _objects;
    
    _objects = (id *) objc_malloc(sizeof(id) * length);
     
    for (i = 0; i < length; i++)
    {
      _objects[i] = nil;
    }
    
    
    for (col = 0; col < _columns; col++)
      for (row = 0; row < _rows; row++)
      {
        _objects[col + row * columns] = origin[col + row * _columns];
      }
    
    objc_free(origin);
    
    _columns = columns;
    _length  = length;
  }
  
  return self;
}

//
// Return the number of rows
// 
// @return the number of rows
// 

- (int) rows
{
  return _rows;
}

//
// Set the number of rows
// 
// @param rows     the number of rows
// 
// @return the object
// 

- (DTable *) rows :(int) rows
{
  if (rows > _rows)
  {
    long  length = rows * _columns;
    long  i;
    long  row, col;
    
    id   *origin = _objects;
    
    _objects = (id *) objc_malloc(sizeof(id) * length);
     
    for (i = 0; i < length; i++)
    {
     _objects[i] = nil;
    }
    
    
    for (col = 0; col < _columns; col++)
      for (row = 0; row < _rows; row++)
      {
        _objects[col + row * _columns] = origin[col + row * _columns];
      }
    
    objc_free(origin);
    
    _rows   = rows;
    _length = length;
  }
   
  return self;
}

//
// Check if an index is valid
//
// @param col      the column (0..)
// @param row      the row (0..)
//
// @return is it valid ?
//

- (BOOL) isValid :(int) col :(int) row
{
  return ((col >= 0) && (col < _columns) && (row >= 0) && (row < _rows));
}


//// Main methods

//
// Set an object in the table
//
// @param col      the column (0..)
// @param row      the row (0..)
// @param obj      the object to be set
//
// @return the previous stored object in the table (or nil)
//

- (id) set :(int) col :(int) row :(id) obj
{
  id prev = nil;
  
  if ((col >= 0) && (col < _columns) && (row >= 0) && (row < _rows))
  {
    long index = row * _columns + col;
     
    prev = _objects[index];

    _objects[index] = obj;
  }
  else
  {
    WARNING(DW_ARG_OUT_RANGE, "col/row");
  }
  
  return prev;
}

//
// Get an object from the table
//
// @param col      the column (0..)
// @param row      the row (0..)
//
// @return the object in the table (or nil)
//

- (id) get :(int) col :(int) row
{
  id obj = nil;

  if ((col >= 0) && (col < _columns) && (row >= 0) && (row < _rows))
  {
    obj = _objects[col + row * _columns];
  }
  else
  {
    WARNING(DW_ARG_OUT_RANGE, "col/row");
  }
  
  return obj;
}

//
// Count the number of occurences of an object
//
// @param obj      the object to be counted
//
// @return the number of occurences
//

- (long) count :(id) obj
{
  long i;
  long c = 0L;

  for (i = 0; i <_length; i++)
  {
    if (_objects[i] == obj)
      c++;
  }

  return c;
}

//
// Check if an object is in the table
//
// @param obj      the object to be checked
//
// @return is it in the table ?
//

- (BOOL) has :(id) obj
{
  long i = 0L;
  
  while ((i < _length) && (_objects[i] != obj))
    i++;
   
  return (i < _length);
}

//
// Perform a selector on each object in the table (row after row)
//
// @param sel      the selector
//
// @return the object
//

- (DTable *) each :(SEL) sel
{
  long i;

  for (i = 0L; i < _length; i++)
  {
    if (_objects[i] != nil)
    {
      [_objects[i] perform :sel];
    }
  }

  return self;
}

@end

/*==========================================================================*/
